#region Copyright Jonathan Quiros

//	
// All rights are reserved. Reproduction or transmission in whole or in 
// part, in any form or by any means, electronic, mechanical or otherwise, 
// is prohibited without the prior written consent of the copyright owner.
//
// Filename: GameLogic.cs
//

#endregion

using System;
using System.Collections.Specialized;
using System.Linq;
using System.Collections.Generic;
using Ids.Web.Listing;
using Novagoal.Models;
using Novagoal.Models.Utils;
using Novagoal.Repositories;
using Novagoal.Repositories.Entities;
using Novagoal.Utils;
using Ids.Utils;

namespace Novagoal.Logic
{
    public class GamesLogic : BaseLogic
    {
        #region CONSTANTS

        private const int EXACT_MATCH_VALUE = 3;
        private const int PARTIAL_MATCH_VALUE = 1;
        private const int MVP_MATCH_VALUE = 2;
        private const int GAMES_IN_VIEW = 4;

        #endregion

        #region PROPERTIES

        private readonly IGamesRepository _gameRepository;
        private readonly ITournamentsRepository _tournamentsRepository;

        #endregion

        #region PUBLIC METHODS

        public GamesLogic(IGamesRepository gamesRepository, ITournamentsRepository tournamentsRepository)
        {
            _gameRepository = gamesRepository;
            _tournamentsRepository = tournamentsRepository;
        }

        public GameList GetGames(NameValueCollection filters, string sort, string direction, int currentPage, int pageSize)
        {
            GameListSortField sortField;
            SortDirectionType sortDirection;
            Dictionary<GameListFilter, string> filterList;

            try
            {
                //Sorting based on parameters
                sortField = sort.ToEnumValue<GameListSortField>();
                sortDirection = direction.ToEnumValue<SortDirectionType>();
                filterList = ParseFilters<GameListFilter>(filters);
            }
            catch
            {
                //Default Sorting
                filterList = null;
                sortField = GameListSortField.Id;
                sortDirection = SortDirectionType.Descending;
            }

            var items = _gameRepository.GetGames(filterList, sortField, sortDirection, currentPage, pageSize);
            var count = _gameRepository.GetTotalGames(filterList);

            var gameList = new GameList
            {
                CurrentPage = currentPage,
                Filters = filterList,
                Items = items,
                SortDirection = sortDirection,
                SortField = sortField
            };
            gameList.CalculatePagination(count, pageSize, currentPage);

            return gameList;
        }

        public List<Game> GetGames(int teamId)
        {
            var gameList = new List<Game>();
            var usersRepository = new UsersRepository();
            var system = usersRepository.GetPerson(Enums.SystemUser.Email.ToStringValue());
            var games = _gameRepository.GetGamesByTeam(teamId);
            games = games.Take(5).ToList();
            foreach (var game in games)
            {
                game.ActualResult = _gameRepository.GetResultByPerson(game.Id, system.Id);
                if(game.ActualResult != null)
                {
                    gameList.Add(game);
                }
            }
            return gameList;
        }

        public List<Game> UpcommingGames(int personId)
        {
            var gameList = new List<Game>();
            var tournaments = _tournamentsRepository.GetTournamentsByPerson(personId);
            foreach (var tournament in tournaments)
            {
                var games = _gameRepository.GetUpcomingGamesByTournament(tournament.Id);
                foreach (var game in games)
                {
                    game.PredictedResult = _gameRepository.GetResultByPerson(game.Id, personId) ?? new Result();
                    gameList.Add(game);
                }
            }
            return gameList.OrderBy(g => g.StartedAt)
                .Take(GAMES_IN_VIEW)
                .ToList();
        }

        public List<Game> FinishedGames(int personId)
        {
            var usersRepository = new UsersRepository();
            var system = usersRepository.GetPerson(Enums.SystemUser.Email.ToStringValue());
            var gameList = new List<Game>();
            var tournaments = _tournamentsRepository.GetTournamentsByPerson(personId);
            foreach (var tournament in tournaments)
            {
                var games = _gameRepository.GetFinishedGamesByTournament(tournament.Id);
                foreach (var game in games)
                {
                    game.PredictedResult = _gameRepository.GetResultByPerson(game.Id, personId);
                    if(!game.Enabled)
                    {
                        game.ActualResult = _gameRepository.GetResultByPerson(game.Id, system.Id);
                        gameList.Add(game);
                    }
                }
            }
            return gameList.OrderByDescending(g => g.StartedAt)
                .Take(GAMES_IN_VIEW)
                .ToList();
        }

        public List<Game> GamesInProgress(int personId)
        {
            var gameList = new List<Game>();
            var tournaments = _tournamentsRepository.GetTournamentsByPerson(personId);
            foreach (var tournament in tournaments)
            {
                var games = _gameRepository.GetFinishedGamesByTournament(tournament.Id);
                foreach (var game in games)
                {
                    if (game.Enabled)
                    {
                        var predictedResult = _gameRepository.GetResultByPerson(game.Id, personId);
                        game.PredictedResult = predictedResult ?? new Result();
                        gameList.Add(game);
                    }
                }
            }
            return gameList.OrderByDescending(g => g.StartedAt).ToList();
        }

        public List<Game> AllFinishedGames(int tournamentId)
        {
            var gameList = new List<Game>();
            var usersRepository = new UsersRepository();
            var system = usersRepository.GetPerson(Enums.SystemUser.Email.ToStringValue());

            var games = _gameRepository.GetFinishedGamesByTournament(tournamentId);
            foreach (var game in games)
            {
                game.ActualResult = _gameRepository.GetResultByPerson(game.Id, system.Id);
                if (game.ActualResult != null)
                {
                    gameList.Add(game);
                }
            }
            return gameList.OrderBy(g => g.StartedAt).ToList();
        }

        public Game GetGame(int gameId)
        {
            return _gameRepository.GetGame(gameId);
        }

        public List<Player> GetTeamPlayers(int teamId)
        {
            return _gameRepository.GetPlayersByTeam(teamId);
        }

        public void SaveResults(IEnumerable<Game> games)
        {
            foreach (var game in games)
            {
                var gameEntity = _gameRepository.GetGame(game.Id);
                if(gameEntity.StartedAt < DateTime.UtcNow)
                {
                    throw new ApplicationException(string.Format("The game {0} vs {1} has already begun", gameEntity.TeamA.Name, gameEntity.TeamB.Name));
                }

                _gameRepository.DeleteResult(game.Id, game.PredictedResult.UpdatedBy);
                _gameRepository.SaveResult(game.Id, game.PredictedResult);
            }
        }

        public int Save(Game game)
        {
            game.Enabled = true;
            return game.Id == 0 ? _gameRepository.AddGame(game) : _gameRepository.EditGame(game);
        }

        public Dictionary<Person, Result> CalculateResults(Game game)
        {
            var list = new Dictionary<Person, Result>();
            var people = _gameRepository.GetPeopleWithResult(game.Id);
            foreach (var person in people)
            {
                var result = _gameRepository.GetResultByPerson(game.Id, person.Id);
                result.Points = CompareResults(game.PredictedResult, result);
                list.Add(person, result);
            }
            return list;
        }

        public Dictionary<Person, Result> GetResults(int gameId)
        {
            var usersRepository = new UsersRepository();
            var system = usersRepository.GetPerson(Enums.SystemUser.Email.ToStringValue());

            var list = new Dictionary<Person, Result>();
            var people = _gameRepository.GetPeopleWithResult(gameId).OrderBy(p => p.FirstName);
            foreach (var person in people)
            {
                if (person.Id != system.Id)
                {
                    var result = _gameRepository.GetResultByPerson(gameId, person.Id);
                    list.Add(person, result);
                }
            }
            return list;
        }

        public void CloseGame(Game game)
        {
            var usersRepository = new UsersRepository();
            var system = usersRepository.GetPerson(Enums.SystemUser.Email.ToStringValue());
            game.PredictedResult.UpdatedBy = system.Id;

            var people = _gameRepository.GetPeopleWithResult(game.Id);
            foreach (var person in people)
            {
                var result = _gameRepository.GetResultByPerson(game.Id, person.Id);
                result.Points = CompareResults(game.PredictedResult, result);
                
                //Update the points
                _gameRepository.UpdatePoints(result, game, person);
            }
            _gameRepository.SaveResult(game.Id, game.PredictedResult);
            _gameRepository.CloseGame(game.Id);
        }

        #endregion

        #region PRIVATE METHODS

        private int CompareResults(Result realResult, Result predictedResult)
        {
            var points = 0;

            //Exact Match
            if(predictedResult.TeamAScore == realResult.TeamAScore && predictedResult.TeamBScore == realResult.TeamBScore)
            {
                points += EXACT_MATCH_VALUE;
            }
            else
            {
                //Partial Match - Draw
                if (predictedResult.TeamAScore == predictedResult.TeamBScore && realResult.TeamAScore == realResult.TeamBScore)
                {
                    points += PARTIAL_MATCH_VALUE;
                }
                //Partial Match - Team A Winner
                else if(predictedResult.TeamAScore > predictedResult.TeamBScore && realResult.TeamAScore > realResult.TeamBScore)
                {
                    points += PARTIAL_MATCH_VALUE;
                }
                //Partial Match - Team B Winner
                else if(predictedResult.TeamAScore < predictedResult.TeamBScore && realResult.TeamAScore < realResult.TeamBScore)
                {
                    points += PARTIAL_MATCH_VALUE;
                }
            }
            //MVP Match
            if(predictedResult.Player != null && realResult.Player != null && predictedResult.Player.Id == realResult.Player.Id)
            {
                points += MVP_MATCH_VALUE;
            }
            
            return points;
        }

        #endregion
    }
}